/**
 *******************************************************************************
 * @file gpio_driver.c
 * @author .ISS_AUTO (hudandan@issauto.com)
 * @version 0.1
 * @brief 功能: DI检测 + 按键检测(可使能开启)
 *        MCU: N32G45x
 *
 * @date 2023-05-24    基础功能创建完毕
 *       2023-06-07    移植MCU-N32G45x
 *       2025-03-19    优化宏定义
 *
 * @copyright Copyright (c) 2023 ISSAUTO TECH Co., Ltd. All rights reserved.
 *
 *******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "gpio_driver.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#if (GPIO_DI_CNT_NUM)
static InputTypeDef_t inputInfo[GPIO_DI_CNT_NUM] = {0}; /*!< Defines a Parameter array pointer of input */
#else
static InputTypeDef_t *inputInfo; /*!< Defines a Parameter array pointer of input */
#endif

#if (BUTTONS_CNT_NUM)
static ButtonTypeDef_t buttonInfo[BUTTONS_CNT_NUM] = {0}; /*!< Defines a Parameter array pointer of Button */
static KEY_FIFO_T s_tKey = {0};
#endif

static unsigned char gpio_input_scan_timer = 0;

/* Private function prototypes -----------------------------------------------*/
#if (GPIO_DI_CNT_NUM)
    static InputTypeDef_t InputRegisterOne(GetInputStatusFunc GetFunc, unsigned char Cnt, unsigned char bit_value);
    static void gpio_input_parameter_init(void);
    static void gpio_input_bit_get_with_filter(unsigned char gpio_idx);
#endif

/* Private functions ---------------------------------------------------------*/

#if (BUTTONS_CNT_NUM)

/**
 * @brief 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
 *
 * @param _KeyCode
 */
static void user_gpio_driver_push(unsigned char _KeyCode)
{
    s_tKey.Write++;
    s_tKey.Write %= KEY_FIFO_SIZE;
    s_tKey.Buf[s_tKey.Write] = _KeyCode;
}

/**
 * @brief Get the Key object 从按键FIFO缓冲区读取一个键值
 *
 * @return unsigned char
 */
unsigned char user_button_value_pop(void)
{
    unsigned char ret;

    if (s_tKey.Read != s_tKey.Write) {
        s_tKey.Read ++;
        s_tKey.Read %= KEY_FIFO_SIZE;
        ret = s_tKey.Buf[s_tKey.Read];

        return ret;
    }

    return KEY_NONE;
}

/**
 * @brief 清空按键FIFO缓冲区
 *
 */
void user_button_value_clear(void)
{
    s_tKey.Read = s_tKey.Write;
}

/**
 * @brief Set the Key Param object 设置按键参数
 *
 * @param gpio_idx gpio ID
 * @param KeyID 按键ID，从0开始 (0xFF, 表示无按键ID)
 * @param LongTime 长按事件时间
 * @param RepeatSpeed 连发速度
 */
void user_button_Param_Set(unsigned char gpio_idx,
                           unsigned char KeyID,
                           unsigned short LongTime, unsigned char RepeatSpeed)
{
    inputInfo[gpio_idx].ButtonIdx = KeyID;          //!< 按键ID

    if (KeyID != 0xFF) {
        buttonInfo[KeyID].LongTime    = LongTime / GPIO_SCAN_TIME;      //!< 长按时间 0 表示不检测长按键事件
        buttonInfo[KeyID].LongCount   = 0;             //!< 长按计数器
        buttonInfo[KeyID].RepeatSpeed = RepeatSpeed / GPIO_SCAN_TIME;   //!< 按键连发的速度，0表示不支持连发
        buttonInfo[KeyID].RepeatCount = 0;             //!< 连发计数器
    }
}

#endif

#if (GPIO_DI_CNT_NUM)

/** @addtogroup Reads the specified input port pin.
 * @{
 */

/** @brief Functions: unsigned char Get_DI_1_BitStatus(void);
 */
#define GET_INPUT_BIT_STATUS(x)    \
    static unsigned char Get_DI_##x##_BitStatus(void) \
    { return (GPIO_ReadInputDataBit(GPIO_PORT_DI_##x, GPIO_BIT_DI_##x)); }

#ifdef GPIO_PORT_DI_1
    GET_INPUT_BIT_STATUS(1);
#endif

#ifdef GPIO_PORT_DI_2
    GET_INPUT_BIT_STATUS(2);
#endif

#ifdef GPIO_PORT_DI_3
    GET_INPUT_BIT_STATUS(3);
#endif

#ifdef GPIO_PORT_DI_4
    GET_INPUT_BIT_STATUS(4);
#endif

#ifdef GPIO_PORT_DI_5
    GET_INPUT_BIT_STATUS(5);
#endif

#ifdef GPIO_PORT_DI_6
    GET_INPUT_BIT_STATUS(6);
#endif

#ifdef GPIO_PORT_DI_7
    GET_INPUT_BIT_STATUS(7);
#endif

#ifdef GPIO_PORT_DI_8
    GET_INPUT_BIT_STATUS(8);
#endif

#ifdef GPIO_PORT_DI_9
    GET_INPUT_BIT_STATUS(9);
#endif

#ifdef GPIO_PORT_DI_10
    GET_INPUT_BIT_STATUS(10);
#endif

#ifdef GPIO_PORT_DI_11
    GET_INPUT_BIT_STATUS(11);
#endif

#ifdef GPIO_PORT_DI_12
    GET_INPUT_BIT_STATUS(12);
#endif

#ifdef GPIO_PORT_DI_13
    GET_INPUT_BIT_STATUS(13);
#endif

#ifdef GPIO_PORT_DI_14
    GET_INPUT_BIT_STATUS(14);
#endif

// 按需求补充...
// #ifdef GPIO_PORT_DI_X
//     GET_INPUT_BIT_STATUS(X);
// #endif

/**
 * @}
 */

/**
 * @brief Register DI information
 */
#define GPIO_INPUT_REGISTER(x) { \
        inputInfo[DI_ID_##x] = InputRegisterOne(Get_DI_##x##_BitStatus, INPUT_FILTER_CNT / 2, GPIO_LEVEL_INVALID); \
    }

/**
 * @brief gpio parameter initialization
 *
 * @param none
 */
static void gpio_input_parameter_init(void)
{
    memset((void *)inputInfo, 0x0, sizeof(inputInfo));
    gpio_input_scan_timer = 0;

    /** DI register (No.1 - No.42)
     * [Example]:
     * inputInfo[DO_ID_1] = InputRegisterOne(InputBitStatusGet1, INPUT_FILTER_CNT / 2, GPIO_LEVEL_INVALID);
     **/

    #ifdef GPIO_PORT_DI_1
    GPIO_INPUT_REGISTER(1);
    #endif
    #ifdef GPIO_PORT_DI_2
    GPIO_INPUT_REGISTER(2);
    #endif
    #ifdef GPIO_PORT_DI_3
    GPIO_INPUT_REGISTER(3);
    #endif
    #ifdef GPIO_PORT_DI_4
    GPIO_INPUT_REGISTER(4);
    #endif
    #ifdef GPIO_PORT_DI_5
    GPIO_INPUT_REGISTER(5);
    #endif
    #ifdef GPIO_PORT_DI_6
    GPIO_INPUT_REGISTER(6);
    #endif
    #ifdef GPIO_PORT_DI_7
    GPIO_INPUT_REGISTER(7);
    #endif
    #ifdef GPIO_PORT_DI_8
    GPIO_INPUT_REGISTER(8);
    #endif
    #ifdef GPIO_PORT_DI_9
    GPIO_INPUT_REGISTER(9);
    #endif
    #ifdef GPIO_PORT_DI_10
    GPIO_INPUT_REGISTER(10);
    #endif
    #ifdef GPIO_PORT_DI_11
    GPIO_INPUT_REGISTER(11);
    #endif
    #ifdef GPIO_PORT_DI_12
    GPIO_INPUT_REGISTER(12);
    #endif
    #ifdef GPIO_PORT_DI_13
    GPIO_INPUT_REGISTER(13);
    #endif
    #ifdef GPIO_PORT_DI_14
    GPIO_INPUT_REGISTER(14);
    #endif

    // 按需求补充...
    // #ifdef GPIO_PORT_DI_X
    // GPIO_INPUT_REGISTER(X);
    // #endif
}

/**
 * @brief input 参数设置
 *
 * @param GetFunc 执行函数
 * @param Cnt 滤波时间
 * @param bit_value 电平状态
 * @return InputTypeDef_t
 */
static InputTypeDef_t InputRegisterOne(GetInputStatusFunc GetFunc, unsigned char Cnt, unsigned char bit_value)
{
    InputTypeDef_t p_input;

    ISS_ASSERT(GetFunc != NULL);

    /* Platform-defined GPIO */
    p_input.GetBit = GetFunc; /* 执行函数 */
    p_input.Status = bit_value;
    p_input.Count = Cnt;      /* 滤波时间 */

    #if (BUTTONS_CNT_NUM)
    p_input.ButtonIdx = BUTTON_ID_INVALID; /* Button ID */
    #endif

    return p_input;
}

/** @brief get GPIO pin input status with dithering elimination */
static void gpio_input_bit_get_with_filter(unsigned char gpio_idx)
{
    ISS_ASSERT((gpio_idx < GPIO_DI_CNT_NUM) && (inputInfo[gpio_idx].GetBit != NULL));

    if (inputInfo[gpio_idx].GetBit()) {
        /* "1: High" */
        if (inputInfo[gpio_idx].Count < INPUT_FILTER_CNT) {	//!< ∈[0 , INPUT_FILTER_CNT)
            inputInfo[gpio_idx].Count = INPUT_FILTER_CNT;
        } else if (inputInfo[gpio_idx].Count < 2 * INPUT_FILTER_CNT) { //!< ∈[INPUT_FILTER_CNT , 2*INPUT_FILTER_CNT)
            inputInfo[gpio_idx].Count++;
        } else { //!< >= 2*INPUT_FILTER_CNT

            #if (BUTTONS_CNT_NUM)

            if (inputInfo[gpio_idx].ButtonIdx == BUTTON_ID_INVALID) {

                inputInfo[gpio_idx].Status = GPIO_LEVEL_HIGH;

            } else {
                if (inputInfo[gpio_idx].Status != GPIO_LEVEL_HIGH) {
                    inputInfo[gpio_idx].Status = GPIO_LEVEL_HIGH;

                    /* 发送按钮弹起的消息 */
                    user_gpio_driver_push((unsigned char)(3 * inputInfo[gpio_idx].ButtonIdx + 2)); // 按键弹起
                }

                buttonInfo[inputInfo[gpio_idx].ButtonIdx].LongCount = 0;
                buttonInfo[inputInfo[gpio_idx].ButtonIdx].RepeatCount = 0;
            }

            #else

            inputInfo[gpio_idx].Status = GPIO_LEVEL_HIGH;

            #endif
        }
    } else {
        /* "0: LOW" */
        if (inputInfo[gpio_idx].Count > INPUT_FILTER_CNT) { //!< > INPUT_FILTER_CNT
            inputInfo[gpio_idx].Count = INPUT_FILTER_CNT;
        } else if (inputInfo[gpio_idx].Count != 0) { //!< ∈(0 , INPUT_FILTER_CNT]
            inputInfo[gpio_idx].Count--;
        } else { //!< = 0

            #if (BUTTONS_CNT_NUM)

            if (inputInfo[gpio_idx].ButtonIdx == BUTTON_ID_INVALID) {
                inputInfo[gpio_idx].Status = GPIO_LEVEL_LOW;

            } else {
                if (inputInfo[gpio_idx].Status != GPIO_LEVEL_LOW) {
                    inputInfo[gpio_idx].Status = GPIO_LEVEL_LOW;

                    /* 发送按钮按下的消息 */
                    user_gpio_driver_push((unsigned char)(3 * inputInfo[gpio_idx].ButtonIdx + 1)); // 按键按下
                }

                if (buttonInfo[inputInfo[gpio_idx].ButtonIdx].LongTime > 0) {

                    if (buttonInfo[inputInfo[gpio_idx].ButtonIdx].LongCount < buttonInfo[inputInfo[gpio_idx].ButtonIdx].LongTime) {

                        /* 发送按钮持续按下的消息 */
                        buttonInfo[inputInfo[gpio_idx].ButtonIdx].LongCount ++;

                        if (buttonInfo[inputInfo[gpio_idx].ButtonIdx].LongCount >= buttonInfo[inputInfo[gpio_idx].ButtonIdx].LongTime) {
                            /* 键值放入按键FIFO */
                            user_gpio_driver_push((unsigned char)(3 * inputInfo[gpio_idx].ButtonIdx + 3)); // 长按
                        }

                    } else {
                        if (buttonInfo[inputInfo[gpio_idx].ButtonIdx].RepeatSpeed > 0) {

                            /* 键值连发 */
                            buttonInfo[inputInfo[gpio_idx].ButtonIdx].RepeatCount ++;

                            if (buttonInfo[inputInfo[gpio_idx].ButtonIdx].RepeatCount >= buttonInfo[inputInfo[gpio_idx].ButtonIdx].RepeatSpeed) {
                                buttonInfo[inputInfo[gpio_idx].ButtonIdx].RepeatCount = 0;

                                /* 长按键后，每隔RepeatSpeed时间发送1个按键 */
                                user_gpio_driver_push((unsigned char)(3 * inputInfo[gpio_idx].ButtonIdx + 3)); // 长按
                            }
                        }
                    }
                }
            }

            #else

            inputInfo[gpio_idx].Status = GPIO_LEVEL_LOW;

            #endif
        }
    }
}

#endif

/* ---------------------------------------------------------------------------- *
 *                               GPIO Application                               *
 * ---------------------------------------------------------------------------- */

/** @brief 复位输入信号状态值 */
void reset_gpio_scan_bit(unsigned char gpio_idx)
{
    inputInfo[gpio_idx].Status = 0xFF;
}

/** @brief 获取输入信号状态值 */
unsigned char get_gpio_scan_bit(unsigned char gpio_idx)
{
    return inputInfo[gpio_idx].Status;
}

/** @brief get GPIO pin input status */
unsigned char get_gpio_bit_status(unsigned char gpio_idx)
{
    ISS_ASSERT((gpio_idx < GPIO_DI_CNT_NUM) && (inputInfo[gpio_idx].GetBit != NULL));

    inputInfo[gpio_idx].Status = inputInfo[gpio_idx].GetBit();

    return inputInfo[gpio_idx].Status;
}

/** @brief
unsigned char gpio_input_bit_get_with_filter(unsigned char gpio_idx)
{
	InputTypeDef_t *p_In = &inputInfo[gpio_idx];
	unsigned char DI_sta=0;

	if(gpio_idx >= GPIO_DI_CNT_NUM)
		return 0;

	if (inputInfo[gpio_idx].GetBit() == 1) {
		Delay_ms(10);
		if (inputInfo[gpio_idx].GetBit() == 1)
			DI_sta=1;
	}

	if(DI_sta != inputInfo[gpio_idx].Status)
		inputInfo[gpio_idx].Status=DI_sta;

	return 1; }
*/

/**
 * @brief Scan GPIO pin input status
 *
 * @param none
 */
void user_gpio_driver_scan(void)
{
    unsigned short index;

    if (gpio_input_scan_timer == 0) {
        gpio_input_scan_timer = GPIO_SCAN_TIME;

        /* GPIO Cyclic Scan */
        for (index = 0; index < GPIO_DI_CNT_NUM; index++) {
            gpio_input_bit_get_with_filter(index);
        }
    }
}

/**
 * @brief initialization gpio driver
 *
 * @param none
 */
void user_gpio_driver_init(void)
{
    #if (GPIO_DI_CNT_NUM)
    gpio_input_parameter_init();
    #endif

    #if (BUTTONS_CNT_NUM)
    user_button_value_clear();
    #endif
}

/**
 * @brief Base SysTick Interrupt Service Routine.
 */
void user_gpio_driver_timer_tick_isr(void)
{
    if (gpio_input_scan_timer > 0) gpio_input_scan_timer--;
}

/* END OF FILE ---------------------------------------------------------------*/
